//
// gcs_session.cpp
// gcs
// Grand Central Station, controlling process of the Protea Application Server / manages user sessions
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include <stdio.h>
#include <string.h>
#include "../defines.h"
#include "../OSDep/process.h"
#include "gcs_session.h"

///////////////////////////////////
// GCSSession object
///////////////////////////////////


// Constructor
//
//	Accepts a user-name for the session
//
GCSSession::GCSSession( char* username, int s_id ) {
//	printf( "Creating session for user: '%s'\n", username );
	user = strdup( username );
	id = s_id;

	trans_number = 0;
	app_number = 0;
};



// Reset
//
// Resets the expiration time of the session.  Used each time the session is "used"
//
void GCSSession::Reset( time_t& time_to_expire, int trans, int app ) {
	const long SESSION_TIMEOUT = 5 * 60;

//	printf( "Reset session for user: %s\n", user );

	// Set the expiration period
	timeout = time_to_expire + SESSION_TIMEOUT;

	// Reset the transaction number and app number
	trans_number = trans;
	app_number = app;
};



// CheckTimeTime
//
// Returns true if the session has "timed-out"
//
bool GCSSession::CheckTimeOut( time_t& current_time ) {
//	printf( "Check session timeout for user: %s\n", user );
	return current_time > timeout;
};



///////////////////////////////////
// GCSSessionList object
///////////////////////////////////
GCSSessionsList::GCSSessionsList() {
	sessionlist = NULL;
	count = 0;
};



const int GCSSessionsList::Count() {
	return count;
};

//
// Returns true if any licenses can be reclaimed
//
// Assumption: SessionLock is already locked when entering this function
//
bool GCSSessionsList::Refresh( time_t& current_time ) {
	bool result = false;

	GCSSession* temp = sessionlist;
	while (temp) {
		if (temp->CheckTimeOut( current_time )) {
//			printf( "Expire license for user: %s\n", temp->GetUser() );

			GCSSession* deleteme = temp;

			if (sessionlist == temp) {
				temp = sessionlist = sessionlist->next;
				if (sessionlist)
					sessionlist->prev = NULL;
			} else {
				temp->prev->next = temp->next;
				if (temp->next)
					temp->next->prev = temp->prev;
				temp = temp->next;
			};
			delete deleteme;
			count--;

			result = true;
		} else
			temp = temp->next;
	};

	return result;
};

GCSSession* GCSSessionsList::GetSession( char* username, int& id, int trans, int app ) {
	GCSSession* result = NULL;

	// Get the current time
	time_t current_time = time( NULL );

	// Lock the session list
	session_lock.Lock();
	try {
		// Attempt to find the session based on user name
		result = sessionlist;
		while (result && !(id == result->GetID() && strcmp(result->GetUser(), username)==0))
			result = result->next;

		// If no session found, then attempt to create one
		if (!result) {
			if ( Refresh( current_time )) {
				if (!id)
					id = rand();

				result = new GCSSession( username, id );
				result->next = sessionlist;
				if (sessionlist)
					sessionlist->prev = result;
				result->prev = NULL;
				sessionlist = result;
				count++;
			} else
				result = NULL;
		};
	} catch( ... ) {
		// Unlock the session list
		session_lock.UnLock();

		// Rethrow the error
		throw;
	};
	session_lock.UnLock();

	// If the session was found, reset the timer
	if (result)
		result->Reset( current_time, trans, app );

	// Return the result
	return result;
};

void GCSSessionsList::RemoveSession( char* username, int id ) {
	session_lock.Lock();

	// Expire the session
	GCSSession* temp = sessionlist;
	while (temp) {
		if (temp->GetID() == id && strcmp( temp->GetUser(), username )==0) {
			GCSSession* deleteme = temp;

			if (sessionlist == temp) {
				temp = sessionlist = sessionlist->next;
				if (sessionlist)
					sessionlist->prev = NULL;
			} else {
				temp->prev->next = temp->next;
				if (temp->next)
					temp->next->prev = temp->prev;
				temp = temp->next;
			};
			delete deleteme;
			count--;
			temp = NULL;
		} else
			temp = temp->next;
	};
	session_lock.UnLock();
};

const GCSSession* GCSSessionsList::GetFirstSession() {
	return sessionlist;
};
